#include "classFileParser.h"

void ClassFileParser::parse_stream(ClassFileStream * stream)
{

    InstanceKlass *instanceKlass = new InstanceKlass;
    // Magic value
    u4 magic = stream->get_u4_fast();

    // Version numbers
    u2 minor_version = stream->get_u2_fast();
    u2 major_version = stream->get_u2_fast();

    instanceKlass->set_minor_version(minor_version);
    instanceKlass->set_major_version(major_version);

    ConstantPool *cp = parse_constant_pool(stream);
    instanceKlass->set_constants(cp);

    u2 access_flags = stream->get_u2_fast();
    instanceKlass->set_access_flags(AccessFlags(access_flags));

    u2 _this_class = stream->get_u2_fast();
    instanceKlass->set_this_class_index(_this_class);

    // SUPERKLASS
    u2 _super_class_index = stream->get_u2_fast();

    // Interfaces
    u2 _itfs_len = stream->get_u2_fast();
    parse_interfaces(stream,_itfs_len);

    parse_fields(stream);

    parse_methods(stream,cp);

    parse_classfile_attributes(stream,cp);

    this->klass = instanceKlass;
}

InstanceKlass* ClassFileParser::create_instance_klass(){
    klass->set_methods(_methods);
    return klass;
}

ConstantPool *ClassFileParser::parse_constant_pool(ClassFileStream * stream)
{

    ClassFileStream *cfs = stream;

    cfs->skip_u1_fast(103);
    const u1 *ss = cfs->current();
    cfs->skip_u1_fast(-103);

    u2 size = cfs->get_u2_fast();

    ConstantPool *cp = new ConstantPool(size);

    for (int index = 1; index < size; index++)
    {
        u1 tag = cfs->get_u1_fast();
        switch (tag)
        {
        case JVM_CONSTANT_Class:
        {
            const u2 name_index = cfs->get_u2_fast();
            cp->klass_index_at_put(index, name_index);
            break;
        }
        case JVM_CONSTANT_Utf8:
        {
            u2 utf8_length = cfs->get_u2_fast();

            char *s = (char *)malloc(utf8_length + 1);
            s[utf8_length] = '\0';

            cfs->copy(s, utf8_length);
            cp->utf8_index_at_put(index, s);

            printf("JVM_CONSTANT_Utf8=>%s\n", s);
            break;
        }
        case JVM_CONSTANT_Fieldref:
        {
            const u2 class_index = cfs->get_u2_fast();
            const u2 name_and_type_index = cfs->get_u2_fast();
            cp->field_at_put(index, class_index, name_and_type_index);
            break;
        }
        case JVM_CONSTANT_Integer:
        {
            const u4 bytes = cfs->get_u4_fast();
            cp->int_at_put(index, (int)bytes);
            break;
        }

        case JVM_CONSTANT_Double:
        {
            const u8 bytes = cfs->get_u8_fast();
            cp->double_at_put(index, bytes);
            index++; // Skip entry following eigth-byte constant, see JVM book p. 98
            break;
        }

        case JVM_CONSTANT_String:
        {
            const u2 string_index = cfs->get_u2_fast();
            cp->string_index_at_put(index, string_index);
            break;
        }

        case JVM_CONSTANT_Methodref:
        {
            // class_index, name_and_type_index, tag/access_flags
            const u2 class_index = cfs->get_u2_fast();
            const u2 name_and_type_index = cfs->get_u2_fast();
            cp->method_at_put(index, class_index, name_and_type_index);
            break;
        }

        case JVM_CONSTANT_NameAndType:
        {
            const u2 name_index = cfs->get_u2_fast();
            const u2 signature_index = cfs->get_u2_fast();
            cp->name_and_type_at_put(index, name_index, signature_index);
            break;
        }

        default:
            ERROR_PRINT("Unknown constant tag %u in class file %s", tag, cfs->source());
            exit(1);
            break;
        }
    }

    return cp;
}

void ClassFileParser::parse_interfaces(ClassFileStream * stream,
                                       const int itfs_len)
{

    for (int i = 0; i < itfs_len; i++)
    {
        int index = stream->get_u2_fast();
    }
}

void ClassFileParser::parse_fields(ClassFileStream * stream)
{
    const u2 len = stream->get_u2_fast();

    for (size_t i = 0; i < len; i++)
    {
        /* code */
    } 

}

void ClassFileParser::parse_methods(ClassFileStream * stream,ConstantPool* cp){
    const u2 len = stream->get_u2_fast();
    //_methods = new Method* [len];
    _methods = new (len)Array<Method*>(len);
    for (int i = 0; i < len; i++)
    {
        Method* method = parse_method(stream,cp);
        _methods->at_put(i,method);
    }

}

Method* ClassFileParser::parse_method(ClassFileStream* cfs,
    ConstantPool* cp){
    Method* method = new Method;
    method->set_access_flags(AccessFlags(cfs->get_u2_fast()));
    method->set_name_index(cfs->get_u2_fast());
    method->set_signature_index(cfs->get_u2_fast());

    // Parse code and exceptions attribute
    u2 method_attributes_count = cfs->get_u2_fast();
    
    u2 max_stack = 0;
    u2 max_locals = 0;
    u4 code_length = 0;
    while (method_attributes_count--)
    {
        const u2 method_attribute_name_index = cfs->get_u2_fast();
        const u4 method_attribute_length = cfs->get_u4_fast();

        char* method_attribute_name = cp->utf8_at(method_attribute_name_index);

        if(strcmp(method_attribute_name ,"Code") == 0){
            max_stack = cfs->get_u2_fast();
            max_locals = cfs->get_u2_fast();
            code_length = cfs->get_u4_fast();

            u1* code_start = cfs->current();
            ConstMethod* const_method = ConstMethod::allocate(code_length);
            method->set_const_method(const_method);
            const_method->set_code(code_start);
            cfs->skip_u1_fast(code_length);

            

            u2 exception_table_length = cfs->get_u2_fast();
            u2 code_attributes_count = cfs->get_u2_fast();
            while (code_attributes_count--) {
                const u2 code_attribute_name_index = cfs->get_u2_fast();
                const u4 code_attribute_length = cfs->get_u4_fast();

                char* code_attribute_name = cp->utf8_at(code_attribute_name_index);

                if(strcmp(code_attribute_name,"LineNumberTable") == 0){
                    
                    parse_linenumber_table(code_attribute_length, code_length);
                } else if(strcmp(code_attribute_name, "LocalVariableTable") == 0 ){
                    parse_localvariable_table(cfs);
                }

                    //先解析到这 可以用了
            }
        }
        
    }  

    return method;
}

void ClassFileParser::parse_linenumber_table(u4 code_attribute_length,
                                             u4 code_length) {
    ClassFileStream* cfs = _stream;
    u2 num_entries = cfs->get_u2_fast();
    while (num_entries--)
    {
        const u2 bci  = cfs->get_u2_fast(); // start_pc
        const u2 line = cfs->get_u2_fast(); // line_number
    }
                                                  
}

void ClassFileParser::parse_localvariable_table(ClassFileStream* cfs){

    const u2 localvariable_table_length = cfs->get_u2_fast();
    for(int i = 0 ; i < localvariable_table_length ; i++ ){
        const u2 start_pc = cfs->get_u2_fast();
        const u2 length = cfs->get_u2_fast();
        const u2 name_index = cfs->get_u2_fast();
        const u2 descriptor_index = cfs->get_u2_fast();
        const u2 index = cfs->get_u2_fast();
        // Assign to a u4 to avoid overflow
        const u4 end_pc = (u4)start_pc + (u4)length;
    }
    
}

void ClassFileParser::parse_classfile_attributes(ClassFileStream* cfs,ConstantPool* cp){
    u2 attributes_count = cfs->get_u2_fast();
    // Iterate over attributes
    while (attributes_count--) {
        const u2 attribute_name_index = cfs->get_u2_fast();
        const u4 attribute_length = cfs->get_u4_fast();

        char* method_attribute_name = cp->utf8_at(attribute_name_index);
        //
        if(strcmp(method_attribute_name,"SourceFile") == 0 ){
            parse_classfile_sourcefile_attribute(cfs,cp);
        }

    }
}


void ClassFileParser::parse_classfile_sourcefile_attribute(ClassFileStream* cfs,ConstantPool* cp){

    const u2 source_index = cfs->get_u2_fast();
    char* source_name = cp->utf8_at(source_index);
    printf("source-index:%s\n",source_name);
}


